import java.net.DatagramPacket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;

public class ResponseBuilder {
    private HashMap<String, String> inMap;
    private DatagramPacket inPacket;
    private String srcIP;

    public ResponseBuilder(HashMap<String, String> inMap, DatagramPacket inPacket, String srcIP) {
        this.inMap = inMap;
        this.inPacket = inPacket;
        this.srcIP = srcIP;
    }

    public byte[] buildA() {
        int flags;
        if (Server.dbgLvFlag >= 1) {
            System.out.println(srcIP);
            System.out.println("in packet: " + Arrays.toString(inPacket.getData()));
        }
        int i = 0;
        int length = inPacket.getLength();
        byte[] data = new byte[length + 16];
        for (i = 0; i < length; i++) {
            data[i] = inPacket.getData()[i];
        }
        if (srcIP.equals("0.0.0.0")) {
            flags = 0x8183;
        } else {
            flags = 0x8180;
        }
        data[2] = (byte) ((flags >> 8) & 0xFF);
        data[3] = (byte) (flags & 0xFF);
        data[7] = 0x1;
        data[length] = (byte) 0xc0;
        data[length + 1] = (byte) 0x0c;
        data[length + 2] = (byte) 0x0;
        data[length + 3] = (byte) QueryType.A;
        data[length + 4] = (byte) 0x0;
        data[length + 5] = (byte) ClassType.IN;
        data[length + 6] = (byte) 0x0;
        data[length + 7] = (byte) 0x0;
        data[length + 8] = (byte) 0x0;
        data[length + 9] = (byte) 0xe0;
        data[length + 10] = (byte) 0x0;
        data[length + 11] = (byte) 0x4;
        String[] ip = srcIP.split("[.]");
        if (Server.dbgLvFlag >= 1) {
            System.out.println(Arrays.toString(ip));
            System.out.println("Response: " + Arrays.toString(data));
        }

        data[length + 12] = (byte) Integer.parseInt(ip[0]);
        data[length + 13] = (byte) Integer.parseInt(ip[1]);
        data[length + 14] = (byte) Integer.parseInt(ip[2]);
        data[length + 15] = (byte) Integer.parseInt(ip[3]);
        return data;
    }

    public byte[] buildAAAA() {
        int flags;
        if (Server.dbgLvFlag >= 1) {
            System.out.println(srcIP);
            System.out.println("in packet: " + Arrays.toString(inPacket.getData()));
        }
        int i = 0;
        int length = inPacket.getLength();
        byte[] data = new byte[length + 28];
        for (i = 0; i < length; i++) {
            data[i] = inPacket.getData()[i];
        }
        boolean isZero = true;
        byte[] ipv6byte = ipv6StrToBytes(srcIP);
        for (i = 0; i < 16; i++) {
            if (ipv6byte[i] != 0)
                isZero = false;
        }
        if (isZero) {
            flags = 0x8183;
        } else {
            flags = 0x8180;
        }
        data[2] = (byte) ((flags >> 8) & 0xFF);
        data[3] = (byte) (flags & 0xFF);
        data[7] = 0x1;
        data[length] = (byte) 0xc0;
        data[length + 1] = (byte) 0x0c;
        data[length + 2] = (byte) 0x0;
        data[length + 3] = (byte) QueryType.AAAA;
        data[length + 4] = (byte) 0x0;
        data[length + 5] = (byte) ClassType.IN;
        data[length + 6] = (byte) 0x0;
        data[length + 7] = (byte) 0x0;
        data[length + 8] = (byte) 0x0;
        data[length + 9] = (byte) 0xe0;
        data[length + 10] = (byte) 0x0;
        data[length + 11] = (byte) 0x10;
        for (i = 0; i < 16; i++) {
            data[length + 12 + i] = ipv6byte[i];
        }
        return data;
    }

    public static byte[] hexToByte(int hex, int byteLength) {
        byte[] src = Integer.toBinaryString(hex).getBytes(StandardCharsets.UTF_8);
        byte[] res = new byte[byteLength];
        for (int i = 0; i < byteLength; i++) {
            if (i < byteLength - src.length) {
                res[i] = 0;
            } else {
                src[i - byteLength + src.length] -= 48;
                res[i] = src[i - byteLength + src.length];
            }
        }
        return res;
    }

    public static byte[] ipv6StrToBytes(String ipv6) {
        byte[] res = new byte[16];
        int partition1 = 0, partition2 = 0, validNum = 0;
        String[] v6strs = ipv6.split("[:]");
        ArrayList<byte[]> part1List = new ArrayList<>();
        ArrayList<byte[]> part2List = new ArrayList<>();
        boolean flag = true;
        for (String s : v6strs) {
            if (s.length() == 0) {
                flag = false;
            } else {
                validNum++;
                int temp = 0;
                for (int i = 0; i < s.length(); i++) {
                    int val = hexToDec(s.charAt(i));
                    temp = temp * 16 + val;
                }
                byte[] converted = new byte[2];
                converted[0] = (byte) ((temp >> 8) & 0xFF);
                converted[1] = (byte) (temp & 0xFF);
                if (flag) {
                    part1List.add(converted);
                } else {
                    part2List.add(converted);
                }
            }
        }
        int cursor = 0;
        for (byte[] b : part1List) {
            res[cursor] = b[0];
            cursor++;
            res[cursor] = b[1];
            cursor++;
        }
        for (int i = 0; i < 8 - validNum; i++) {
            res[cursor] = 0x0;
            cursor++;
            res[cursor] = 0x0;
            cursor++;
        }
        for (byte[] b : part2List) {
            res[cursor] = b[0];
            cursor++;
            res[cursor] = b[1];
            cursor++;
        }
        return res;
    }

    public byte[] ipToBytes(String ip) {
        byte[] res = new byte[32];
        writeBytesFromBytes(res, 0, hexToByte(Integer.parseInt(ip.split(".")[0]), 8));
        writeBytesFromBytes(res, 8, hexToByte(Integer.parseInt(ip.split(".")[1]), 8));
        writeBytesFromBytes(res, 16, hexToByte(Integer.parseInt(ip.split(".")[2]), 8));
        writeBytesFromBytes(res, 24, hexToByte(Integer.parseInt(ip.split(".")[3]), 8));
        return res;
    }

    public static int hexToDec(char c) {
        if (c >= '0' && c <= '9') {
            return c - 48;
        } else if (c >= 'a' && c <= 'f') {
            return c - 87;
        } else if (c >= 'A' && c <= 'F') {
            return c - 55;
        } else {
            return 0;
        }
    }

    public static void writeBytesFromHex(byte[] target, int cursor, int hex) {
        byte[] src = Integer.toBinaryString(hex).getBytes(StandardCharsets.UTF_8);
        for (int i = 0; i < src.length; i++) {
            target[i + cursor] = src[i];
        }
    }

    public static void writeBytesFromBytes(byte[] target, int cursor, byte[] src) {
        for (int i = 0; i < src.length; i++) {
            target[i + cursor] = src[i];
        }
    }

    public static void main(String[] args) {
        if (Server.dbgLvFlag >= 1)
            System.out.println(Arrays.toString(ResponseBuilder.ipv6StrToBytes("2001:DB8:2de::e13")));
    }
}
